home *** CD-ROM | disk | FTP | other *** search
/ Clickx 96 / Clickx 96.iso / software / tools / tool / xbmc-10.1.exe / addons / script.module.pil / lib / PIL / PngImagePlugin.py < prev    next >
Encoding:
Python Source  |  2009-06-13  |  17.0 KB  |  621 lines

  1. #
  2. # The Python Imaging Library.
  3. # $Id$
  4. #
  5. # PNG support code
  6. #
  7. # See "PNG (Portable Network Graphics) Specification, version 1.0;
  8. # W3C Recommendation", 1996-10-01, Thomas Boutell (ed.).
  9. #
  10. # history:
  11. # 1996-05-06 fl   Created (couldn't resist it)
  12. # 1996-12-14 fl   Upgraded, added read and verify support (0.2)
  13. # 1996-12-15 fl   Separate PNG stream parser
  14. # 1996-12-29 fl   Added write support, added getchunks
  15. # 1996-12-30 fl   Eliminated circular references in decoder (0.3)
  16. # 1998-07-12 fl   Read/write 16-bit images as mode I (0.4)
  17. # 2001-02-08 fl   Added transparency support (from Zircon) (0.5)
  18. # 2001-04-16 fl   Don't close data source in "open" method (0.6)
  19. # 2004-02-24 fl   Don't even pretend to support interlaced files (0.7)
  20. # 2004-08-31 fl   Do basic sanity check on chunk identifiers (0.8)
  21. # 2004-09-20 fl   Added PngInfo chunk container
  22. # 2004-12-18 fl   Added DPI read support (based on code by Niki Spahiev)
  23. # 2008-08-13 fl   Added tRNS support for RGB images
  24. # 2009-03-06 fl   Support for preserving ICC profiles (by Florian Hoech)
  25. # 2009-03-08 fl   Added zTXT support (from Lowell Alleman)
  26. # 2009-03-29 fl   Read interlaced PNG files (from Conrado Porto Lopes Gouvua)
  27. #
  28. # Copyright (c) 1997-2009 by Secret Labs AB
  29. # Copyright (c) 1996 by Fredrik Lundh
  30. #
  31. # See the README file for information on usage and redistribution.
  32. #
  33.  
  34. __version__ = "0.9"
  35.  
  36. import re, string
  37.  
  38. import Image, ImageFile, ImagePalette, zlib
  39.  
  40.  
  41. def i16(c):
  42.     return ord(c[1]) + (ord(c[0])<<8)
  43. def i32(c):
  44.     return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24)
  45.  
  46. is_cid = re.compile("\w\w\w\w").match
  47.  
  48.  
  49. _MAGIC = "\211PNG\r\n\032\n"
  50.  
  51.  
  52. _MODES = {
  53.     # supported bits/color combinations, and corresponding modes/rawmodes
  54.     (1, 0): ("1", "1"),
  55.     (2, 0): ("L", "L;2"),
  56.     (4, 0): ("L", "L;4"),
  57.     (8, 0): ("L", "L"),
  58.     (16,0): ("I", "I;16B"),
  59.     (8, 2): ("RGB", "RGB"),
  60.     (16,2): ("RGB", "RGB;16B"),
  61.     (1, 3): ("P", "P;1"),
  62.     (2, 3): ("P", "P;2"),
  63.     (4, 3): ("P", "P;4"),
  64.     (8, 3): ("P", "P"),
  65.     (8, 4): ("LA", "LA"),
  66.     (16,4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available
  67.     (8, 6): ("RGBA", "RGBA"),
  68.     (16,6): ("RGBA", "RGBA;16B"),
  69. }
  70.  
  71.  
  72. # --------------------------------------------------------------------
  73. # Support classes.  Suitable for PNG and related formats like MNG etc.
  74.  
  75. class ChunkStream:
  76.  
  77.     def __init__(self, fp):
  78.  
  79.         self.fp = fp
  80.         self.queue = []
  81.  
  82.         if not hasattr(Image.core, "crc32"):
  83.             self.crc = self.crc_skip
  84.  
  85.     def read(self):
  86.         "Fetch a new chunk. Returns header information."
  87.  
  88.         if self.queue:
  89.             cid, pos, len = self.queue[-1]
  90.             del self.queue[-1]
  91.             self.fp.seek(pos)
  92.         else:
  93.             s = self.fp.read(8)
  94.             cid = s[4:]
  95.             pos = self.fp.tell()
  96.             len = i32(s)
  97.  
  98.         if not is_cid(cid):
  99.             raise SyntaxError, "broken PNG file (chunk %s)" % repr(cid)
  100.  
  101.         return cid, pos, len
  102.  
  103.     def close(self):
  104.         self.queue = self.crc = self.fp = None
  105.  
  106.     def push(self, cid, pos, len):
  107.  
  108.         self.queue.append((cid, pos, len))
  109.  
  110.     def call(self, cid, pos, len):
  111.         "Call the appropriate chunk handler"
  112.  
  113.         if Image.DEBUG:
  114.             print "STREAM", cid, pos, len
  115.         return getattr(self, "chunk_" + cid)(pos, len)
  116.  
  117.     def crc(self, cid, data):
  118.         "Read and verify checksum"
  119.  
  120.         crc1 = Image.core.crc32(data, Image.core.crc32(cid))
  121.         crc2 = i16(self.fp.read(2)), i16(self.fp.read(2))
  122.         if crc1 != crc2:
  123.             raise SyntaxError, "broken PNG file"\
  124.                 "(bad header checksum in %s)" % cid
  125.  
  126.     def crc_skip(self, cid, data):
  127.         "Read checksum.  Used if the C module is not present"
  128.  
  129.         self.fp.read(4)
  130.  
  131.     def verify(self, endchunk = "IEND"):
  132.  
  133.         # Simple approach; just calculate checksum for all remaining
  134.         # blocks.  Must be called directly after open.
  135.  
  136.         cids = []
  137.  
  138.         while 1:
  139.             cid, pos, len = self.read()
  140.             if cid == endchunk:
  141.                 break
  142.             self.crc(cid, ImageFile._safe_read(self.fp, len))
  143.             cids.append(cid)
  144.  
  145.         return cids
  146.  
  147.  
  148. # --------------------------------------------------------------------
  149. # PNG chunk container (for use with save(pnginfo=))
  150.  
  151. class PngInfo:
  152.  
  153.     def __init__(self):
  154.         self.chunks = []
  155.  
  156.     def add(self, cid, data):
  157.         self.chunks.append((cid, data))
  158.  
  159.     def add_text(self, key, value, zip=0):
  160.         if zip:
  161.             import zlib
  162.             self.add("zTXt", key + "\0\0" + zlib.compress(value))
  163.         else:
  164.             self.add("tEXt", key + "\0" + value)
  165.  
  166. # --------------------------------------------------------------------
  167. # PNG image stream (IHDR/IEND)
  168.  
  169. class PngStream(ChunkStream):
  170.  
  171.     def __init__(self, fp):
  172.  
  173.         ChunkStream.__init__(self, fp)
  174.  
  175.         # local copies of Image attributes
  176.         self.im_info = {}
  177.         self.im_text = {}
  178.         self.im_size = (0,0)
  179.         self.im_mode = None
  180.         self.im_tile = None
  181.         self.im_palette = None
  182.  
  183.     def chunk_iCCP(self, pos, len):
  184.  
  185.         # ICC profile
  186.         s = ImageFile._safe_read(self.fp, len)
  187.         # according to PNG spec, the iCCP chunk contains:
  188.         # Profile name  1-79 bytes (character string)
  189.         # Null separator        1 byte (null character)
  190.         # Compression method    1 byte (0)
  191.         # Compressed profile    n bytes (zlib with deflate compression)
  192.         i = string.find(s, chr(0))
  193.         if Image.DEBUG:
  194.             print "iCCP profile name", s[:i]
  195.             print "Compression method", ord(s[i])
  196.         comp_method = ord(s[i])
  197.         if comp_method != 0:
  198.             raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method)
  199.         try:
  200.             icc_profile = zlib.decompress(s[i+2:])
  201.         except zlib.error:
  202.             icc_profile = None # FIXME
  203.         self.im_info["icc_profile"] = icc_profile
  204.         return s
  205.  
  206.     def chunk_IHDR(self, pos, len):
  207.  
  208.         # image header
  209.         s = ImageFile._safe_read(self.fp, len)
  210.         self.im_size = i32(s), i32(s[4:])
  211.         try:
  212.             self.im_mode, self.im_rawmode = _MODES[(ord(s[8]), ord(s[9]))]
  213.         except:
  214.             pass
  215.         if ord(s[12]):
  216.             self.im_info["interlace"] = 1
  217.         if ord(s[11]):
  218.             raise SyntaxError, "unknown filter category"
  219.         return s
  220.  
  221.     def chunk_IDAT(self, pos, len):
  222.  
  223.         # image data
  224.         self.im_tile = [("zip", (0,0)+self.im_size, pos, self.im_rawmode)]
  225.         self.im_idat = len
  226.         raise EOFError
  227.  
  228.     def chunk_IEND(self, pos, len):
  229.  
  230.         # end of PNG image
  231.         raise EOFError
  232.  
  233.     def chunk_PLTE(self, pos, len):
  234.  
  235.         # palette
  236.         s = ImageFile._safe_read(self.fp, len)
  237.         if self.im_mode == "P":
  238.             self.im_palette = "RGB", s
  239.         return s
  240.  
  241.     def chunk_tRNS(self, pos, len):
  242.  
  243.         # transparency
  244.         s = ImageFile._safe_read(self.fp, len)
  245.         if self.im_mode == "P":
  246.             i = string.find(s, chr(0))
  247.             if i >= 0:
  248.                 self.im_info["transparency"] = i
  249.         elif self.im_mode == "L":
  250.             self.im_info["transparency"] = i16(s)
  251.         elif self.im_mode == "RGB":
  252.             self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:])
  253.         return s
  254.  
  255.     def chunk_gAMA(self, pos, len):
  256.  
  257.         # gamma setting
  258.         s = ImageFile._safe_read(self.fp, len)
  259.         self.im_info["gamma"] = i32(s) / 100000.0
  260.         return s
  261.  
  262.     def chunk_pHYs(self, pos, len):
  263.  
  264.         # pixels per unit
  265.         s = ImageFile._safe_read(self.fp, len)
  266.         px, py = i32(s), i32(s[4:])
  267.         unit = ord(s[8])
  268.         if unit == 1: # meter
  269.             dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5)
  270.             self.im_info["dpi"] = dpi
  271.         elif unit == 0:
  272.             self.im_info["aspect"] = px, py
  273.         return s
  274.  
  275.     def chunk_tEXt(self, pos, len):
  276.  
  277.         # text
  278.         s = ImageFile._safe_read(self.fp, len)
  279.         try:
  280.             k, v = string.split(s, "\0", 1)
  281.         except ValueError:
  282.             k = s; v = "" # fallback for broken tEXt tags
  283.         if k:
  284.             self.im_info[k] = self.im_text[k] = v
  285.         return s
  286.  
  287.     def chunk_zTXt(self, pos, len):
  288.  
  289.         # compressed text
  290.         s = ImageFile._safe_read(self.fp, len)
  291.         k, v = string.split(s, "\0", 1)
  292.         comp_method = ord(v[0])
  293.         if comp_method != 0:
  294.             raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method)
  295.         import zlib
  296.         self.im_info[k] = self.im_text[k] = zlib.decompress(v[1:])
  297.         return s
  298.  
  299. # --------------------------------------------------------------------
  300. # PNG reader
  301.  
  302. def _accept(prefix):
  303.     return prefix[:8] == _MAGIC
  304.  
  305. ##
  306. # Image plugin for PNG images.
  307.  
  308. class PngImageFile(ImageFile.ImageFile):
  309.  
  310.     format = "PNG"
  311.     format_description = "Portable network graphics"
  312.  
  313.     def _open(self):
  314.  
  315.         if self.fp.read(8) != _MAGIC:
  316.             raise SyntaxError, "not a PNG file"
  317.  
  318.         #
  319.         # Parse headers up to the first IDAT chunk
  320.  
  321.         self.png = PngStream(self.fp)
  322.  
  323.         while 1:
  324.  
  325.             #
  326.             # get next chunk
  327.  
  328.             cid, pos, len = self.png.read()
  329.  
  330.             try:
  331.                 s = self.png.call(cid, pos, len)
  332.             except EOFError:
  333.                 break
  334.             except AttributeError:
  335.                 if Image.DEBUG:
  336.                     print cid, pos, len, "(unknown)"
  337.                 s = ImageFile._safe_read(self.fp, len)
  338.  
  339.             self.png.crc(cid, s)
  340.  
  341.         #
  342.         # Copy relevant attributes from the PngStream.  An alternative
  343.         # would be to let the PngStream class modify these attributes
  344.         # directly, but that introduces circular references which are
  345.         # difficult to break if things go wrong in the decoder...
  346.         # (believe me, I've tried ;-)
  347.  
  348.         self.mode = self.png.im_mode
  349.         self.size = self.png.im_size
  350.         self.info = self.png.im_info
  351.         self.text = self.png.im_text # experimental
  352.         self.tile = self.png.im_tile
  353.  
  354.         if self.png.im_palette:
  355.             rawmode, data = self.png.im_palette
  356.             self.palette = ImagePalette.raw(rawmode, data)
  357.  
  358.         self.__idat = len # used by load_read()
  359.  
  360.  
  361.     def verify(self):
  362.         "Verify PNG file"
  363.  
  364.         if self.fp is None:
  365.             raise RuntimeError("verify must be called directly after open")
  366.  
  367.         # back up to beginning of IDAT block
  368.         self.fp.seek(self.tile[0][2] - 8)
  369.  
  370.         self.png.verify()
  371.         self.png.close()
  372.  
  373.         self.fp = None
  374.  
  375.     def load_prepare(self):
  376.         "internal: prepare to read PNG file"
  377.  
  378.         if self.info.get("interlace"):
  379.             self.decoderconfig = self.decoderconfig + (1,)
  380.  
  381.         ImageFile.ImageFile.load_prepare(self)
  382.  
  383.     def load_read(self, bytes):
  384.         "internal: read more image data"
  385.  
  386.         while self.__idat == 0:
  387.             # end of chunk, skip forward to next one
  388.  
  389.             self.fp.read(4) # CRC
  390.  
  391.             cid, pos, len = self.png.read()
  392.  
  393.             if cid not in ["IDAT", "DDAT"]:
  394.                 self.png.push(cid, pos, len)
  395.                 return ""
  396.  
  397.             self.__idat = len # empty chunks are allowed
  398.  
  399.         # read more data from this chunk
  400.         if bytes <= 0:
  401.             bytes = self.__idat
  402.         else:
  403.             bytes = min(bytes, self.__idat)
  404.  
  405.         self.__idat = self.__idat - bytes
  406.  
  407.         return self.fp.read(bytes)
  408.  
  409.  
  410.     def load_end(self):
  411.         "internal: finished reading image data"
  412.  
  413.         self.png.close()
  414.         self.png = None
  415.  
  416.  
  417. # --------------------------------------------------------------------
  418. # PNG writer
  419.  
  420. def o16(i):
  421.     return chr(i>>8&255) + chr(i&255)
  422.  
  423. def o32(i):
  424.     return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255)
  425.  
  426. _OUTMODES = {
  427.     # supported PIL modes, and corresponding rawmodes/bits/color combinations
  428.     "1":   ("1", chr(1)+chr(0)),
  429.     "L;1": ("L;1", chr(1)+chr(0)),
  430.     "L;2": ("L;2", chr(2)+chr(0)),
  431.     "L;4": ("L;4", chr(4)+chr(0)),
  432.     "L":   ("L", chr(8)+chr(0)),
  433.     "LA":  ("LA", chr(8)+chr(4)),
  434.     "I":   ("I;16B", chr(16)+chr(0)),
  435.     "P;1": ("P;1", chr(1)+chr(3)),
  436.     "P;2": ("P;2", chr(2)+chr(3)),
  437.     "P;4": ("P;4", chr(4)+chr(3)),
  438.     "P":   ("P", chr(8)+chr(3)),
  439.     "RGB": ("RGB", chr(8)+chr(2)),
  440.     "RGBA":("RGBA", chr(8)+chr(6)),
  441. }
  442.  
  443. def putchunk(fp, cid, *data):
  444.     "Write a PNG chunk (including CRC field)"
  445.  
  446.     data = string.join(data, "")
  447.  
  448.     fp.write(o32(len(data)) + cid)
  449.     fp.write(data)
  450.     hi, lo = Image.core.crc32(data, Image.core.crc32(cid))
  451.     fp.write(o16(hi) + o16(lo))
  452.  
  453. class _idat:
  454.     # wrap output from the encoder in IDAT chunks
  455.  
  456.     def __init__(self, fp, chunk):
  457.         self.fp = fp
  458.         self.chunk = chunk
  459.     def write(self, data):
  460.         self.chunk(self.fp, "IDAT", data)
  461.  
  462. def _save(im, fp, filename, chunk=putchunk, check=0):
  463.     # save an image to disk (called by the save method)
  464.  
  465.     mode = im.mode
  466.  
  467.     if mode == "P":
  468.  
  469.         #
  470.         # attempt to minimize storage requirements for palette images
  471.  
  472.         if im.encoderinfo.has_key("bits"):
  473.  
  474.             # number of bits specified by user
  475.             n = 1 << im.encoderinfo["bits"]
  476.  
  477.         else:
  478.  
  479.             # check palette contents
  480.             n = 256 # FIXME
  481.  
  482.         if n <= 2:
  483.             bits = 1
  484.         elif n <= 4:
  485.             bits = 2
  486.         elif n <= 16:
  487.             bits = 4
  488.         else:
  489.             bits = 8
  490.  
  491.         if bits != 8:
  492.             mode = "%s;%d" % (mode, bits)
  493.  
  494.     # encoder options
  495.     if im.encoderinfo.has_key("dictionary"):
  496.         dictionary = im.encoderinfo["dictionary"]
  497.     else:
  498.         dictionary = ""
  499.  
  500.     im.encoderconfig = (im.encoderinfo.has_key("optimize"), dictionary)
  501.  
  502.     # get the corresponding PNG mode
  503.     try:
  504.         rawmode, mode = _OUTMODES[mode]
  505.     except KeyError:
  506.         raise IOError, "cannot write mode %s as PNG" % mode
  507.  
  508.     if check:
  509.         return check
  510.  
  511.     #
  512.     # write minimal PNG file
  513.  
  514.     fp.write(_MAGIC)
  515.  
  516.     chunk(fp, "IHDR",
  517.           o32(im.size[0]), o32(im.size[1]),     #  0: size
  518.           mode,                                 #  8: depth/type
  519.           chr(0),                               # 10: compression
  520.           chr(0),                               # 11: filter category
  521.           chr(0))                               # 12: interlace flag
  522.  
  523.     if im.mode == "P":
  524.         chunk(fp, "PLTE", im.im.getpalette("RGB"))
  525.  
  526.     if im.encoderinfo.has_key("transparency"):
  527.         if im.mode == "P":
  528.             transparency = max(0, min(255, im.encoderinfo["transparency"]))
  529.             chunk(fp, "tRNS", chr(255) * transparency + chr(0))
  530.         elif im.mode == "L":
  531.             transparency = max(0, min(65535, im.encoderinfo["transparency"]))
  532.             chunk(fp, "tRNS", o16(transparency))
  533.         elif im.mode == "RGB":
  534.             red, green, blue = im.encoderinfo["transparency"]
  535.             chunk(fp, "tRNS", o16(red) + o16(green) + o16(blue))
  536.         else:
  537.             raise IOError("cannot use transparency for this mode")
  538.  
  539.     if 0:
  540.         # FIXME: to be supported some day
  541.         chunk(fp, "gAMA", o32(int(gamma * 100000.0)))
  542.  
  543.     dpi = im.encoderinfo.get("dpi")
  544.     if dpi:
  545.         chunk(fp, "pHYs",
  546.               o32(int(dpi[0] / 0.0254 + 0.5)),
  547.               o32(int(dpi[1] / 0.0254 + 0.5)),
  548.               chr(1))
  549.  
  550.     info = im.encoderinfo.get("pnginfo")
  551.     if info:
  552.         for cid, data in info.chunks:
  553.             chunk(fp, cid, data)
  554.  
  555.     # ICC profile writing support -- 2008-06-06 Florian Hoech
  556.     if im.info.has_key("icc_profile"):
  557.         # ICC profile
  558.         # according to PNG spec, the iCCP chunk contains:
  559.         # Profile name  1-79 bytes (character string)
  560.         # Null separator        1 byte (null character)
  561.         # Compression method    1 byte (0)
  562.         # Compressed profile    n bytes (zlib with deflate compression)
  563.         try:
  564.             import ICCProfile
  565.             p = ICCProfile.ICCProfile(im.info["icc_profile"])
  566.             name = p.tags.desc.get("ASCII", p.tags.desc.get("Unicode", p.tags.desc.get("Macintosh", p.tags.desc.get("en", {}).get("US", "ICC Profile")))).encode("latin1", "replace")[:79]
  567.         except ImportError:
  568.             name = "ICC Profile"
  569.         data = name + "\0\0" + zlib.compress(im.info["icc_profile"])
  570.         chunk(fp, "iCCP", data)
  571.  
  572.     ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)])
  573.  
  574.     chunk(fp, "IEND", "")
  575.  
  576.     try:
  577.         fp.flush()
  578.     except:
  579.         pass
  580.  
  581.  
  582. # --------------------------------------------------------------------
  583. # PNG chunk converter
  584.  
  585. def getchunks(im, **params):
  586.     """Return a list of PNG chunks representing this image."""
  587.  
  588.     class collector:
  589.         data = []
  590.         def write(self, data):
  591.             pass
  592.         def append(self, chunk):
  593.             self.data.append(chunk)
  594.  
  595.     def append(fp, cid, *data):
  596.         data = string.join(data, "")
  597.         hi, lo = Image.core.crc32(data, Image.core.crc32(cid))
  598.         crc = o16(hi) + o16(lo)
  599.         fp.append((cid, data, crc))
  600.  
  601.     fp = collector()
  602.  
  603.     try:
  604.         im.encoderinfo = params
  605.         _save(im, fp, None, append)
  606.     finally:
  607.         del im.encoderinfo
  608.  
  609.     return fp.data
  610.  
  611.  
  612. # --------------------------------------------------------------------
  613. # Registry
  614.  
  615. Image.register_open("PNG", PngImageFile, _accept)
  616. Image.register_save("PNG", _save)
  617.  
  618. Image.register_extension("PNG", ".png")
  619.  
  620. Image.register_mime("PNG", "image/png")
  621.